home *** CD-ROM | disk | FTP | other *** search
/ Aminet 13 / Aminet 13 - August 1996.iso / Aminet / docs / mags / gadget25.lha / AmigaGadget25 / Texte / prg.ReadDirInPCQ / prg.ReadDirInPCQ
Encoding:
Text File  |  1996-06-18  |  18.1 KB  |  450 lines

  1. #Titel Programmieren / Verzeichnisse mit PCQ lesen
  2. #Logo gadget25:Pinsel/AG.Prog
  3. #Font Losse 16
  4. #C31
  5.         Verzeichnisse mit PCQ lesen
  6. #Font topaz 8
  7. #C10
  8.            ein neuer Teil des sporadisch fortgeführten PCQ-Kurses
  9.  
  10. #C21
  11. Nachdem im Rahmen dieses Kurses bereits die grundlegenden Elemente von
  12. "Intuition" vorgestellt, die Funktionsweise doppelt verketteter Listen
  13. erklärt und auch eine Laderoutine für IFF-Dateien entwickelt wurde, soll
  14. diesmal ein völlig anderes Thema angesprochen werden - es geht um einige
  15. Funktionen der dos.library.
  16.  
  17. Obwohl wir im Zeitalter von Kickstart 3.1, der asl.library und MUI leben,
  18. dem Programmierer mithin bereits zahlreiche Funktionen zur Verfügung
  19. gestellt werden, wenn es um die Handhabung von Verzeichnisinhalten geht (wie
  20. z.B. den asl-Filerequester), können nach wie vor Situationen eintreten,
  21. in denen einem nichts anderes übrig bleibt, als sich selbst die
  22. entsprechenden Informationen zu besorgen. Dies soll im folgenden erläutert
  23. und anhand eines eigenen (primitiven) "Dir"-Ersatzes dargestellt werden.
  24. Doch fangen wir von ganz vorne an.
  25.  
  26. Ein Datenträger - sei es Festplatte, CD-ROM oder Diskette - kann
  27. bekanntermaßen nicht nur mit einzelnen Dateien gefüllt, sondern der
  28. besseren Strukturierung wegen auch in einzelne Verzeichnisse und
  29. hierarchisch untergeordnete Unterverzeichnisse unterteilt werden. Das kennt
  30. man jedenfalls von der Workbench-Ebene durch die dort verwendeten
  31. Schubladen-Symbole, zu denen auch tatsächlich entsprechende Verzeichnisse
  32. auf dem Datenträger gehören (im Gegensatz zur weitaus umständlicheren
  33. Organisationsstruktur etwa von OS/2 Warp oder Windows 3.xx). Um die
  34. wenig spektakuläre Materie doch nochmals ein weiteres Stückchen zu
  35. verdeutlichen - skizziert man den Aufbau eines Datenträgers, ergibt sich
  36. folgende Struktur:
  37. #Seitenende
  38. #C10
  39. #Y+30
  40.                        Datenträger-"Root"-Verzeichnis
  41.        ____________________________|___________________________________
  42.       |     |          |           |            |                      |
  43. Datei 1 Datei 2 [...] Datei n Verzeichnis 1 Verzeichnis 2 [...] Verzeichnis N
  44.                                    |            |                      |
  45.                                    |  [entsprechend dem Verlauf bei Verz. 1]
  46.                                    |
  47.        ____________________________|___________________________________
  48.       |     |          |           |            |                      |
  49. Datei a Datei b [...] Datei x Verzeichnis a Verzeichnis b [...] Verzeichnis X
  50.                                    |            |                      |
  51.                                  [...]        [...]                  [...]
  52. #Seitenende
  53. #C21
  54. Bei kleineren Datenträgern wie Disketten ergibt sich aus Platzgründen eine
  55. Beschränkung der Komplexität, so daß man sich problemlos merken kann, wo
  56. man was auf ihm findet. Anders sieht das mit größeren Datenträgern aus und
  57. spätestens ab der 1 GByte-Festplatte muß man schon über ein außergewöhnliches
  58. Gedächtnis verfügen, um sich den Inhalt sämtlicher Verzeichnisse und
  59. Unterverzeichnisse merken zu können. Da diese Lösung auch äußerst
  60. unpraktisch ist, gibt es seit jeher zahlreichen Hilfen zur Bewältigung
  61. der Dateienflut - die bekannteste dürfte dabei natürlich die Workbench sein,
  62. mit deren Hilfe es möglich ist, sich intuitiv durch die Verästelungen der
  63. "Schubladen"-Hierarchie zu hangeln. Als zusätzliche Hilfe dienen auch all
  64. die Dateimanager, deren bekanntester Vertreter sicherlich "DirOpus" sein
  65. dürfte. Doch all das sind Aufsätze. Auf der untersten Ebene, dem CLI, bzw.
  66. der Shell, gilt es mit äußerst bescheidenen Mitteln der Dateienflut Herr
  67. zu werden. Wichtigstes Hilfsmittel ist der "dir"-Befehl, der dem Anwender
  68. den kompletten Inhalt des aktuellen Verzeichnisses in Textform ausgibt.
  69. Da er über einige Parameter verfügt (etwa Joker wie "#?" oder das Flag
  70. "-a", mit dem rekursiv zusätzlich sämtliche Unterverzeichnisse und deren
  71. Inhalt ausgegeben werden), läßt sich bereits sehr gut mit ihm arbeiten.
  72. Findige Programmierer haben den ursprünglichen Befehl natürlich noch weiter
  73. ausgebaut und verbessert, so daß man im FD-Pool weitaus mächtigere
  74. "dir"-Clones finden kann. Doch all das ist nur etwas für den Anwender. Was
  75. aber macht der Programmierer, dessen Programm den Inhalt eines
  76. Verzeichnisses herausfinden soll ?
  77.  
  78. Ansatzpunkt ist die Funktion
  79.  
  80. #C10
  81.   Function ExNext(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
  82. #C21
  83.  
  84. der "dos.library". Ihre beiden Parameter sind
  85.  
  86. #C10
  87. lock:
  88. #C21
  89. #Y-10
  90.       Lock (bzw. ein BCPL-Zeiger auf diesen Lock) eines Verzeichnisses,
  91.       dessen Inhalt "gescannt" werden soll.
  92.  
  93. #C10
  94. info:
  95. #C21
  96. #Y-10
  97.       Zeiger auf einen FileInfoBlock, in dem die Informationen zum jeweils
  98.       gelesenen Verzeichniseintrag zu finden sind.
  99.  
  100. Das hilft uns noch nicht viel weiter, denn es stellen sich zwei Fragen:
  101.  
  102. 1.) Wie bekomme ich so einen "Lock" auf ein Verzeichnis ? Und was ist das
  103.     überhaupt ?
  104. 2.) Was steht alles in dem FileInfoBlock und wie handhabe ich ihn ?
  105.  
  106. #C10
  107. zu 1.):
  108. #C21
  109. #Y-10
  110.         Grundsätzlich ist ein "Lock" das Ergebnis der konsequenten
  111. Multitasking-Ausrichtung des Amiga-Betriebssystems. Man stelle sich folgende
  112. Situation vor: ein Programm durchforstet ein Verzeichnis namens "texte".
  113. Da sich darin sehr viele Dateien befinden, dauert das Spielchen eine
  114. ganze Zeit. Währenddessen ändert ein heimtückischer Anwender in einem
  115. parallel laufenden CLI-Prozess den Verzeichnisnamen "texte" in "reingelegt".
  116. Sucht das Programm nun einfach die nächste Datei im Verzeichnis "texte",
  117. dann muß es - da dieses Verzeichnis ja so nicht mehr existiert - scheitern,
  118. wenn es dieses Verzeichnis nur über den Namen identifizieren würde.
  119. Diese Gefahr wird mit den Locks umgangen. Denn so wird ein Verzeichnis
  120. (und auch eine Datei) nicht mehr über ihren Namen zugeordnet, es entsteht
  121. vielmehr eine Art "Zeiger", der nun jedoch nicht auf eine Datenstruktur
  122. im Speicher, sondern auf eine Datei auf einem Datenträger zeigt. Ändert
  123. sich der Name der Datei hat das keine Auswirkungen auf die korrekte
  124. Bezeichnung der Datei durch den "Zeiger", den Lock. Einen solchen
  125. "Zeiger" auf ein Verzeichnis oder ein File erhält man ganz problemlos
  126. als Rückgabewert der Funktion
  127.  
  128. #C10
  129.       Function Lock(name : String; accessmode : Integer) : FileLock;
  130.  
  131. #C10
  132.                "name"                                            "accessmode"
  133. #C21
  134. #Y-10
  135. Dabei steht in        der Name der Datei/des Verzeichnisses. Und
  136. enthält einen der beiden Werte
  137.  
  138. #C10
  139.     ACCESS_READ         = -2;
  140. #C21
  141. #Y-10                                   oder
  142.     ACCESS_WRITE        = -1;
  143.  
  144. #C21
  145. Synonym für               kann auch               verwendet werden - in
  146. #C10
  147. #Y-10
  148.             "ACCESS_READ"           "SHARED_LOCK"
  149. #C21
  150. diesem Zugriffsmode können auch andere Programme einen entsprechenden
  151. Lock auf die Datei/das Verzeichnis erhalten. Das geht nicht bei
  152. #C10
  153. "ACCESS_WRITE"             "EXCLUSIVE_LOCK"
  154. #C21
  155. #Y-10
  156.                (entspricht                 ). Hier hat man den Lock auf die
  157. entsprechende Datei/das entsprechende Verzeichnis für sich alleine - was
  158. etwa bei Schreibzugriffen natürlich sinnvoll ist: wird eine Datei z.B. erst
  159. erstellt, so würden Programme, die zeitgleich dazu die Größe der Datei
  160. zu ermitteln versuchen, lediglich kaum sinnvolle Werte erhalten. Solche
  161. Verwirrungen werden mit einem WRITE-Lock ausgeschlossen.
  162.  
  163. Der Rückgabewert der Funktion ist nun der langgesuchte Lock. Bevor wir aber
  164. in der Theorie weiterschreiten, soll noch darauf hingewiesen werden, daß
  165. man niemals vergessen sollte, ein Lock (insbesondere ein WRITE-Lock) mit
  166.  
  167. #C10
  168.                   Procedure UnLock(lock : FileLock);
  169. #C21
  170.  
  171. wieder freizugeben. Nicht nur, daß man mit falschen Parametern bei einem
  172. Aufruf der "Lock"-Funktionen erhebliche Konfusion im AmigaDOS stiften kann -
  173. ein nicht geschlossener Lock kann auch sonst unangenehm werden. So lassen
  174. sich beispielsweise Dateien, auf die noch ein offener Lock besteht, nicht
  175. löschen. Man sollte mithin immer dafür sorgen, daß ein selbstgeschriebenes
  176. Programm vor dem Verlassen sauber alle vorher erstellten Locks wieder
  177. freigibt.
  178.  
  179. #C10
  180. zu 2.)
  181. #C21
  182. #Y-10
  183.        Ein Lock auf das zu durchsuchende Verzeichnis hilft uns an sich aber
  184. ja noch nicht weiter. Nun kommt der FileInfoBlock zum Zug. Es handelt sich
  185. bei ihm um eine Struktur der "dos.library", die wie folgt aussieht:
  186.  
  187. #C10
  188.     FileInfoBlock = record
  189.         fib_DiskKey      : Integer;
  190.         fib_DirEntryType : Integer;
  191. #Y-10
  192. #C21                                    Dieser Wert zeigt an, ob es sich
  193.                                         bei dem "File" um ein Verzeichnis
  194.                                         (dann ist er größer Null) oder um
  195.                                         eine normale Datei handelt (dann
  196.                                         gilt fib_DirEntryType<0).
  197. #C10
  198.         fib_FileName    : Array [0..107] of Char;
  199. #C21
  200.                                         Hier erhält man den - auch
  201.                                         hinsichtlich etwaiger Groß- und
  202.                                         Kleinschreibung - korrekten
  203.                                         und vollständigen Dateinamen.
  204.                                         Damit man mit dem umständlichen
  205.                                         Char-Feld arbeiten kann, empfiehlt
  206.                                         es sich, einen Zeiger auf den
  207.                                         Feldeintrag 0 einer String-Variablen
  208.                                         zuzordnen. Das funktioniert
  209.                                         problemlos, da fib_FileName mit
  210.                                         einem Nullbyte endet, was ja auch
  211.                                         für PCQ das Ende eines Strings
  212.                                         symbolisiert.
  213.  
  214. #C10
  215.         fib_Protection  : Integer;
  216. #C21
  217. #Y-10
  218.                                         Hinter diesem Wert verbergen sich
  219.                                         die Protection-Flags (zu sehen etwa
  220.                                         über die "Information" im WB-Menü
  221.                                         oder mittels des "List"-Befehls im
  222.                                         CLI). Die Flags sind dabei als
  223.                                         "BitMask" angeordnet, d.h. ein
  224.                                         gesetztes Bit an einer bestimmten
  225.                                         Position bedeutet ein gesetztes
  226.                                         Flag. Die vier wichtigsten
  227.                                         Protection-Flags sind "Read",
  228.                                         "Write", "Execute" und "Delete".
  229.                                         Diese vier verbergen sich in
  230.                                         genau dieser Reihenfolge in den
  231.                                         untersten vier Bits des Integer-
  232.                                         Wertes. D.h.: ist Bit 0 gesetzt,
  233.                                         dann ist die Datei löschgeschützt,
  234.                                         ist es nicht gesetzt, kann sie
  235.                                         gelöscht werden. Anderes Beispiel:
  236.                                         Ist Bit 3 gesetzt, kann die Datei
  237.                                         nicht gelesen werden. Die
  238.                                         einzelnen Protection-Bits sind auch
  239.                                         in "Libraries/Dos.I" definiert.
  240.                                         Für die vier hier etwas näher
  241.                                         vorgestellten, heissen die
  242.                                         Flag-Werte
  243.  
  244. #C10
  245.                                         FIBF_READ           = 8;
  246.                                         FIBF_WRITE          = 4;
  247.                                         FIBF_EXECUTE        = 2;
  248.                                         FIBF_DELETE         = 1;
  249. #C21
  250.  
  251.                                         Im Programm kann man die einzelnen
  252.                                         Protection-Bits dann so abfragen:
  253.  
  254. #C10
  255.                                         BitGesetzt:=
  256.                                            ((IntWert AND FIB-Wert)=FIB-Wert)
  257.         fib_EntryType   : Integer;
  258.         fib_Size        : Integer;
  259. #C21
  260. #Y-10
  261.                                         Dieser Wert enthält die Größe der
  262.                                         Datei in Bytes.
  263.  
  264. #C10
  265.         fib_NumBlocks   : Integer;
  266. #C21
  267. #Y-10
  268.                                         Diesem Wert kann man entnehmen,
  269.                                         wieviele Blöcke die Datei belegt.
  270.  
  271. #C10
  272.         fib_Date        : DateStampRec;
  273. #C21
  274. #Y-10
  275.                                         Hier steht das Datum, an dem die
  276.                                         Datei das letzte Mal geändert wurde.
  277.                                         Allerdings ist die Entschlüsselung
  278.                                         nicht so ganz einfach, da ein
  279.                                         DateStamp nur die Differenz zum
  280.                                         1.1.1978 in Tagen enthält...
  281.  
  282. #C10
  283.         fib_Comment     : Array [0..79] of Char;
  284. #C21
  285.                                         Hier steht ein etwaiger Kommentar
  286.                                         zu der Datei. Zur programmtechnischen
  287.                                         Handhabung gilt das schon beim
  288.                                         Filenamen gesagte.
  289. #C10
  290.         fib_Reserved    : Array [0..35] of Char;
  291.     end;
  292. #C21
  293.  
  294. Diese Informationsflut ist ja schon recht verlockend. Um sie jedoch für
  295. uns nutzen zu können, müssen wir zunächst entsprechenden Speicherplatz
  296. (mit "AllocMem") belegen und dann die Funktion
  297.  
  298. #C10
  299.   Function Examine(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
  300. #C21
  301.  
  302. mit dem vorher erstellten Lock auf das zu durchforstende Verzeichnis und
  303. dem Zeiger auf den vorher ja allozierten Speicher für den FileInfoBlock (FIB)
  304. aufrufen. Dies ist notwendig, damit der FIB die notwendigen Ausgangsdaten
  305. enthält. Um nun die einzelnen Verzeichnisinhalte einzulesen, brauchen wir
  306. nichts weiter tun, als so lange die Funktion
  307.  
  308. #C10
  309.    Function ExNext(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
  310. #C21
  311.  
  312. mit genau denselben Parametern wie zuvor bei der einmaligen Verwendung von
  313. "Examine" aufzurufen, bis der Rückgabewert FALSE ist. Zwischen zwei
  314. Aufrufen kann dann der jeweils sich anpassende Inhalt der FIB-Struktur
  315. ausgewertet, d.h. insbesondere Informationen zum Verzeichnisinhalt
  316. ausgegeben oder weiteren Unterverzeichnissen gefolgt werden. Letzteres
  317. ist natürlich nicht ganz so einfach, programmtechnisch allerdings recht
  318. elegant mit einer einfacher Rekursion (sprich: dem Aufruf der ReadDir-
  319. Routine durch sich selbst mit neuen Parametern) gelöst werden. Das geht
  320. zwar problemlos, jedoch sollte man sich zwei Dinge vor Augen halten:
  321.  
  322. 1. Bei Rekursionen wird in PCQ der Stack belastet, d.h. bei sehr stark
  323.    verschachtelten (die Betonung liegt auf _sehr_) Verzeichnisstrukturen
  324.    kann es hier zu Problemen kommen.
  325.  
  326. 2. Bearbeitet man nicht das aktuelle, sondern ein vom Benutzer zu
  327.    bestimmendes Verzeichnis, so muß man bei rekursiven Routinenaufrufen
  328.    den Verzeichnisnamen "manuell" um den Namen des Unterverzeichnisses
  329.    ergänzen. Damit macht man sich aber die nicht auf eine über den Namen
  330.    erfolgende Zuordnung beschränkte "Lock"-Technik über ein
  331.    Hintertürchen wieder kaputt.
  332.  
  333. Beide Probleme können selbstverständlich gelöst werden. Im Rahmen dieses
  334. Kurzüberblickes soll es jedoch mit dem Hinweis auf ihre Existenz ein
  335. Bewenden haben.
  336.  
  337. Liefert "ExNext" schließlich FALSE, gibt man den für den FIB benutzten
  338. Speicherbereich wieder frei, ruft "UnLock" auf und ist fertig. Wie das
  339. ganze in der Praxis aussieht, soll mit dem nun anschließenden Programm
  340. demonstriert werden.
  341. #Seitenende
  342. #C10
  343. PROGRAM ReadDir;
  344.  
  345. {
  346.   Demonstrationsprogramm für das Kapitel "Einladen eines Verzeichnisinhaltes
  347.   des AmigaGadget-PCQ-Kurses
  348.  
  349.   written by Andreas Neumann 08.06.1996
  350.  
  351.   das Programm wird über CLI mit dem Namen des anzuzeigenden Verzeichnisses
  352.   aufgerufen und gibt sämtliche Dateien dieses Directories, sowie die
  353.   Dateien in eventuellen Unterverzeichnissen, aus
  354.  
  355.   Achtung: nicht bei sehr stark verschachtelten Verzeichnissen verwenden,
  356.            da ansonsten der Stack "überlaufen" und das System abstürzen
  357.            könnte !
  358. }
  359. #Seitenende
  360. {$I "Include:libraries/dosextens.i" }
  361. {$I "Include:utils/Parameters.i" }
  362. {$I "Include:utils/Stringlib.i" }
  363. {$I "Include:exec/memory.i" }
  364.  
  365. VAR dirname :   String;
  366.  
  367. PROCEDURE ZeigeVerzeichnisAn (einruecken : BYTE; verzeichnis : STRING);
  368.  
  369. VAR
  370.     zvLock  :   FileLock;
  371.     zvInfo  :   FileInfoBlockPtr;
  372.     zvi     :   INTEGER;
  373.     zvStr2,
  374.     zvStr   :   String;
  375.  
  376. BEGIN
  377.  zvInfo:=AllocMem (SIZEOF(FileInfoBlock),MEMF_CLEAR+MEMF_PUBLIC);
  378.  IF zvInfo<>NIL THEN
  379.  BEGIN
  380.   zvLock:=Lock (verzeichnis,ACCESS_READ);
  381.   IF zvLock<>NIL THEN
  382.   BEGIN
  383.    IF Examine (zvLock,zvInfo)=FALSE THEN
  384.    BEGIN
  385.     UnLock (zvLock);
  386.     zvLock:=NIL;
  387.    END
  388.    ELSE
  389.    BEGIN
  390.     WHILE (ExNext (zvLock , zvInfo)) DO
  391.     BEGIN
  392.      zvi:=einruecken;
  393.      WHILE zvi>0 DO
  394.      BEGIN
  395.       WRITE (' ');
  396.       Dec(zvi);
  397.      END;
  398.      zvStr:=Adr(zvInfo^.fib_FileName);
  399.      IF zvInfo^.fib_DirEntryType>0 THEN
  400.      BEGIN
  401.       WRITELN (zvStr,":");
  402.       zvStr2:=AllocString(255);
  403.       IF zvStr2<>NIL THEN
  404.       BEGIN
  405.        StrCpy (zvStr2,verzeichnis);
  406.        IF (zvStr2[StrLen(zvStr2)-1]<>':') AND
  407.           (zvStr2[StrLen(zvStr2)-1]<>'/') THEN
  408.        IF (StrLen(verzeichnis)>0) THEN
  409.         StrCat (zvStr2,"/");
  410.        StrCat (zvStr2,zvStr);
  411.        ZeigeVerzeichnisAn (einruecken+4,zvStr2);
  412.        FreeString (zvstr2);
  413.       END;
  414.      END
  415.      ELSE
  416.       WRITELN (zvStr);
  417.     END;
  418.     UnLock (zvLock);
  419.     zvLock:=NIL;
  420.    END;
  421.   END;
  422.   FreeMem (zvInfo,SIZEOF(FileInfoBlock));
  423.  END;
  424. END;
  425.  
  426. BEGIN
  427.  dirname:=AllocString (255);
  428.  IF dirname<>NIL THEN
  429.  BEGIN
  430.   GetParam (1,dirname);
  431.   ZeigeVerzeichnisAn (0,dirname);
  432.   FreeString (dirname);
  433.  END;
  434. END.
  435.  
  436. #C21
  437. Man sieht: es ist wirklich recht problemlos zu realisieren. Das Demolisting
  438. ist dabei bewußt kurz gehalten - Erweiterungen, etwa die Anzeige von
  439. filespezifischen Informationen oder die Konfiguration über Flags oder
  440. die Sortierung der gelesenen Verzeichnisinhalte, dürften programmtechnisch
  441. kein ernsthaftes Problem sein und empfehlen sich dem interessierten
  442. Lernenden zur Übung...
  443.  
  444. Wenn es Fragen gibt, Kritik, Anregungen oder was auch immer, dann
  445. ist das "AmigaGadget" der richtige Ort, um sie loszuwerden. Für heute
  446. soll es das gewesen sein. Viel Spaß beim Herumprobieren mit den neu
  447. kennengelernten Routinen wünscht
  448.  
  449. #Pinsel gadget25:pinsel/an rechts
  450.